#!/usr/bin/env python 
# -*- coding: utf-8 -*-
# @Time    : 2023/3/3 10:58
# @Author  : WJQ
# @Site    : 
# @File    : driver.py
# @Software: PyCharm
from abc import abstractmethod
from typing import Optional, List
from appium import webdriver
import frida


import conf
from appium.options.android import UiAutomator2Options
from utils.common import CrawlerLogger, get_platform_version, kill_app


class DriverHandler(object):
    '''
    webdriver handler
    '''

    def __init__(self, applets_name, version_number, port=4723):
        self.driver: Optional[webdriver] = None
        self.device = frida.get_device_manager().get_device(conf.CLIENT_SIDE_CONF.DEVICE_ID)
        self.applets_name = applets_name
        self.session = None
        self.appid = None

        self.CAPS = {
            'automationName' : 'UiAutomator2',
            "platformName" : "Android",
            # "deviceName": "8ABY0H7HM",
            # "platformVersion": "12",
            # 不重置应用 很重要不然会被风控
            "noReset" : True,
            "unicodeKeyboard" : True,
            "resetKeyboard" : True,
            # make session waits for next command much longer, default 60 seconds.
            "newCommandTimeout" : 10000,
            # "chromedriverExecutable": r"D:\Python\WorkPlace\chrome\crawlMiniAPP\miniAPP-Crawler\chromeDriver\chromedriver.exe"
            # "chromedriverExecutable" : r"/home/octave/WorkPlace/miniAPP-Crawler/chromeDriver/chromedriver86"
        }
        # self.CAPS['chromedriverExecutable'] = f"/home/octave/WorkPlace/miniAPP-Crawler/chromeDriver/chromedriver{version_number}"
        self.CAPS['chromedriverExecutable'] = conf.chromedriverExecutable
        self.CAPS['deviceName'] = conf.CLIENT_SIDE_CONF.DEVICE_ID
        self.CAPS['platformVersion'] = get_platform_version(conf.CLIENT_SIDE_CONF.DEVICE_ID)
        options = UiAutomator2Options().load_capabilities(self.CAPS)
        self.driver = webdriver.Remote(f"http://localhost:{port}", options=options)
        # self.driver = webdriver.Remote(f"http://host.docker.internal:{port}", options=options)
        self.driver.implicitly_wait(5)
        conf.CLIENT_SIDE_CONF.PHONE_WIDTH = self.driver.get_window_size()['width']
        conf.CLIENT_SIDE_CONF.PHONE_HEIGHT = self.driver.get_window_size()['height']
        CrawlerLogger.info(f"[*] Appium 连接成功...")

    def _reset_program(self):
        self.driver.reset()

    def attach_to_app ( self, app_id, fileName ) :
        if self.session :
            return
        try :
            # 尝试查找已运行的进程
            running_processes = self.device.enumerate_processes()
            target_process = None
            for process in running_processes :
                if process.name == app_id :
                    target_process = process
                    break

            # 根据进程是否存在来决定是附加还是启动新进程
            if target_process is None :
                CrawlerLogger.debug(f"[-] 无法找到 {app_id}，尝试启动应用。")
                pid = self.device.spawn([app_id])
                self.session = self.device.attach(pid)
                self.device.resume(pid)
            else :
                CrawlerLogger.info(f"[+] 发现已运行的 {app_id}，附加进程。")
                self.session = self.device.attach(target_process.pid)

            # 加载 JavaScript 代码
            js_file_path = f'browser/js/{fileName}.js'
            with open(js_file_path, 'r', encoding='utf-8') as file:
                js_code = file.read()
            self.script = self.session.create_script(js_code)
            self.script.load()
        except FileNotFoundError:
            CrawlerLogger.error(f"JavaScript file not found: {js_file_path}")
            raise
        except UnicodeDecodeError:
            CrawlerLogger.error(f"Failed to decode JavaScript file: {js_file_path}")
            raise
        except Exception as e :
            CrawlerLogger.debug(f"[-] 发生错误：{e}")

    def detach_from_app(self):
        if self.session:
            self.session.detach()
            self.session = None

    @abstractmethod
    def find_top_window(self) -> None:
        '''
        baidu,wechat : 切换成当前driver的window handle成最顶层的window
        :return:
        '''
        pass

    @abstractmethod
    def switch_to_webview_context(self) ->None:
        '''
        baidu,wechat : 切换成当前driver的context成webview
        :return:
        '''
        pass

    def switch_to_native_app_context(self) -> None:
        '''
        切换成当前driver的context成native app
        :return:
        '''
        if self.driver.current_context != "NATIVE_APP":
            self.driver.switch_to.context("NATIVE_APP")

    @abstractmethod
    def get_url( self ) -> (str, str):
        '''
        获取当前driver的url, 时taro/pages/actions/index.html?query=1形式
        :return:
        '''
        pass

    @abstractmethod
    def get_all_pages(self, file_path) -> List[str]:
        '''
        获取app.json内所有的pages
        :return:
        '''
        pass

    @abstractmethod
    def enter_mini_program(self, applets_name) -> None:
        '''
        进入对应小程序
        :return:
        '''
        pass

    @abstractmethod
    def get_app_json(self, applets_name) -> None:
        '''
        获取app.json
        :return:
        '''
        pass

    @abstractmethod
    def go_to_target_url( self , target_title) -> None:
        '''
        进入目标title, 包含参数
        :return:
        '''
        pass
